﻿Public Class Matrix

    Friend matrix(,) As Double '定义矩阵变量
    Friend rowbound As Integer '定义数组行数，从0开始
    Friend colbound As Integer '定义数组列数，从0开始

    Public Sub New(ByVal data(,) As Double)
        '首先获得矩阵大小
        rowbound = data.GetUpperBound(0)
        colbound = data.GetUpperBound(1)
        ReDim matrix(rowbound, colbound)

        Dim i As Integer, j As Integer '数组赋值循环变量
        For i = 0 To rowbound '赋值到矩阵变量
            For j = 0 To colbound
                matrix(i, j) = data(i, j)
            Next j
        Next i

    End Sub '构造函数

    Public Sub New(ByVal row As Integer, ByVal col As Integer)
        rowbound = row - 1
        colbound = col - 1
        ReDim matrix(rowbound, colbound)
        For i As Integer = 0 To rowbound
            For j As Integer = 0 To colbound
                matrix(i, j) = 0
            Next
        Next
    End Sub



    Public Sub LetMatrix(ByRef output_(,) As Double, start_row As Integer, start_col As Integer)
        Dim out_row, out_col As Integer
        out_row = output_.GetUpperBound(0)
        out_col = output_.GetUpperBound(1)
        If rowbound + start_row > out_row Then
            Console.WriteLine("the row is out of the range(" & out_row & ")")
        ElseIf colbound + start_col > out_col
            Console.WriteLine("the col is out of the range(" & out_col & ")")
        End If
        Dim i As Integer, j As Integer
        For i = 0 To rowbound
            For j = 0 To colbound
                output_(i + start_row, j + start_col) = matrix(i, j)
            Next j
        Next i
    End Sub  '把类中的矩阵输出到一个二维数组中

    Public Sub SetMatrix(ByVal input_(,) As Double)
        ReDim matrix(input_.GetUpperBound(0), input_.GetUpperBound(1))

        Dim i As Integer, j As Integer '数组赋值循环变量
        For i = 0 To input_.GetUpperBound(0) '赋值到矩阵变量
            For j = 0 To input_.GetUpperBound(1)
                matrix(i, j) = input_(i, j)
            Next j
        Next i
    End Sub '把一个二维数组中数据输出到类中

    '将矩阵进行切片
    Public Function GetPartMartix(start_row As Integer, end_row As Integer, start_col As Integer, end_col As Integer)
        If start_row < 0 Or start_col < 0 Then
            Console.WriteLine("the start row or col can not  < 0")
            Return Nothing
        End If
        If start_row > rowbound Or start_col > colbound Then
            Console.WriteLine("the start row or col can not > (rol:" & rowbound & ") (col:" & colbound & ")")
            Return Nothing
        End If
        If end_row < 0 Or end_col < 0 Then
            Console.WriteLine("the end row or col can not  < 0")
            Return Nothing
        End If
        If end_row > rowbound Or end_col > colbound Then
            Console.WriteLine("the end row or col can not > (rol:" & rowbound & ") (col:" & colbound & ")")
            Return Nothing
        End If
        Dim result(end_row - start_row, end_col - start_col) As Double
        For i As Integer = start_row To end_row
            For j As Integer = start_col To end_col
                result(i - start_row, j - start_col) = matrix(i, j)
            Next
        Next
        Return New Matrix(result)
    End Function

    '矩阵转置
    Public Function GetTranspose() As Matrix
        Dim output_(,) As Double
        ReDim output_(rowbound, colbound)
        LetMatrix(output_, 0, 0)

        Dim i As Integer, j As Integer, t As Double
        For i = 0 To rowbound
            For j = i To colbound
                t = output_(i, j)
                output_(i, j) = output_(j, i)
                output_(j, i) = t
            Next
        Next

        Return New Matrix(output_)
    End Function '得到原矩阵的转置

    Public Function Det() As Double '求行列式的值,矩阵必须是方阵
        Dim value As Double '用于保存行列式的值
        value = 1

        '求值基本思路：通过高斯消去法进行矩阵变换，形成三角行列式
        Dim Ri As Integer, Rj As Integer '用于行列之间循环,Ri为行
        Dim t As Integer '临时变量t用于循环
        Ri = 0
        Rj = 0

        Dim minus As Integer '记录矩阵变换后的正负号
        minus = 1

        Dim k As Double '为了将行列式某些值变成0，必须配个比例系数k，使得 R(a)-k*R(b)=0

        Dim matrix_(,) As Double '定义临时矩阵
        ReDim matrix_(rowbound, colbound)
        For Ri = 0 To rowbound '为了防止原始矩阵被修改，赋值到临时矩阵
            For Rj = 0 To colbound
                matrix_(Ri, Rj) = matrix(Ri, Rj)
            Next Rj
        Next Ri


        For Rj = 0 To rowbound '从最左行开始化上三角
            For Ri = rowbound To Rj + 1 Step -1 '从最下边开始化上三角
                If matrix_(Ri - 1, Rj) = 0 Then
                    SwapRow(matrix_, Ri - 1, Ri) '如果上一行的对应数字为0，则交换这两行行列式
                    minus = minus * -1
                End If
                If matrix_(Ri - 1, Rj) <> 0 Then k = -1 * matrix_(Ri, Rj) / matrix_(Ri - 1, Rj) '比例系数则等于下行数字除以上行
                For t = Rj To rowbound
                    matrix_(Ri, t) = matrix_(Ri, t) + k * matrix_(Ri - 1, t) '高斯变换原理
                Next t
            Next Ri
        Next Rj

        For t = 0 To rowbound '计算上三角行列式的值
            value = value * matrix_(t, t)
        Next
        value = value * minus
        'If value=#IND then value=0
        Return value
    End Function '输出行列式的值

    Public Function Trace() As Double '求矩阵的迹，矩阵必须是方阵
        If rowbound <> colbound Then
            Console.WriteLine("the col and row is different!!")
            Return Nothing
        End If
        Dim s As Double = 0
        For i As Integer = 0 To rowbound
            s += matrix(i, i)
        Next
        Return s
    End Function

    Public Function GetInverse() As Matrix  '求逆矩阵
        '定义类和数据，进行返回
        Dim result As Matrix
        Dim data(,) As Double
        ReDim data(rowbound, colbound)

        Dim matrix_(,) As Double '定义临时矩阵
        Dim tmatrix As Matrix '定义临时矩阵类
        Dim i As Integer, j As Integer '临时变量用于循环
        ReDim matrix_(rowbound, colbound)
        For i = 0 To rowbound '为了防止原始矩阵被修改，赋值到临时矩阵
            For j = 0 To colbound
                matrix_(i, j) = matrix(i, j)
            Next j
        Next i

        If Det() = 0 Then
            Debug.Print("该矩阵没有逆矩阵，因为行列式的值为0")
            Return Nothing
        ElseIf rowbound <> colbound Then
            Debug.Print("该矩阵没有逆矩阵，因为不是方阵")
            Return Nothing
        End If


        '接下来获得伴随矩阵和逆矩阵
        For i = 0 To rowbound
            For j = 0 To colbound
                tmatrix = New Matrix(matrix) '实例化临时矩阵
                tmatrix = tmatrix.DelRow(tmatrix.matrix, i)
                tmatrix = tmatrix.DelCol(tmatrix.matrix, j) '先删除某一行，再删除某一列，得到伴随矩阵子块
                matrix_(j, i) = ((tmatrix.Det) * (-1) ^ (i + j + 2)) / Det()
                '去掉某行某列以得到伴随矩阵子块，并求出其值，除以原矩阵的行列式则得到逆矩阵
            Next j
        Next i

        result = New Matrix(matrix_)

        Return result
    End Function '输出逆矩阵

    Public Shared Operator *(ByVal m1 As Matrix, ByVal m2 As Matrix) As Matrix
        'Dim md1(,) As Double, md2(,) As Double  '存储m1,m2矩阵数据
        'Dim result(,) As Double '存储运算结果
        'Dim t As Double '存储每一个值的临时结果
        'Dim i As Integer, j As Integer, k As Integer
        ''注意：仅仅当坐矩阵列数等于右矩阵行数时才可以进行乘法

        'If m1.colbound <> m2.rowbound Then
        '    Debug.Print("无法进行矩阵乘法运算，矩阵一的列数不等于矩阵二的行数。")
        '    Return Nothing
        'End If

        'ReDim md1(m1.rowbound, m1.colbound)
        'ReDim md2(m2.rowbound, m2.colbound)
        'ReDim result(m1.rowbound, m2.colbound)
        'm1.LetMatrix(md1, 0, 0)
        'm2.LetMatrix(md2, 0, 0)
        'For i = 0 To m2.colbound
        '    For j = 0 To m1.rowbound
        '        t = 0
        '        For k = 0 To m1.colbound
        '            t = t + md1(j, k) * md2(k, i)
        '        Next
        '        result(j, i) = t
        '    Next j
        'Next i
        'Return New Matrix(result)

        Dim md1(,) As Double, md2(,) As Double  '存储m1,m2矩阵数据
        Dim result(,) As Double '存储运算结果
        Dim i As Integer, j As Integer
        '注意：仅仅当左矩阵行列数等于右矩阵行列数时才可以进行乘法

        If m1.colbound <> m2.colbound Or m1.rowbound <> m2.rowbound Then
            Debug.Print("无法进行矩阵乘法运算，矩阵一的行列数不等于矩阵二的行数。")
            Return Nothing
        End If

        ReDim md1(m1.rowbound, m1.colbound)
        ReDim md2(m2.rowbound, m2.colbound)
        ReDim result(m1.rowbound, m1.colbound)
        m1.LetMatrix(md1, 0, 0)
        m2.LetMatrix(md2, 0, 0)
        For i = 0 To m1.rowbound
            For j = 0 To m1.colbound
                result(i, j) = md1(i, j) * md2(i, j)
            Next j
        Next i
        Return New Matrix(result)

    End Operator '重载*运算符，定义矩阵间的乘法运算，计算m1 * m2

    Public Shared Operator +(ByVal m1 As Matrix, ByVal m2 As Matrix) As Matrix
        Dim md1(,) As Double, md2(,) As Double  '存储m1,m2矩阵数据
        Dim result(,) As Double '存储运算结果
        Dim i As Integer, j As Integer
        '注意：只有两个矩阵的长宽一样才可以进行运算

        If m1.rowbound <> m2.rowbound Or m1.colbound <> m2.colbound Then
            Debug.Print("无法进行矩阵加法运算，长宽不一致。")
            Return Nothing
        End If

        ReDim result(m1.rowbound, m1.colbound)
        ReDim md1(m1.rowbound, m1.colbound)
        ReDim md2(m2.rowbound, m2.colbound)
        m1.LetMatrix(md1, 0, 0)
        m2.LetMatrix(md2, 0, 0)

        For i = 0 To m1.rowbound
            For j = 0 To m1.colbound
                result(i, j) = md1(i, j) + md2(i, j)
            Next j
        Next i
        Return New Matrix(result)
    End Operator '重载+运算符，定义矩阵间的加法运算，计算m1 + m2

    Public Shared Operator -(ByVal m1 As Matrix, ByVal m2 As Matrix) As Matrix
        Dim md1(,) As Double, md2(,) As Double  '存储m1,m2矩阵数据
        Dim result(,) As Double '存储运算结果
        Dim i As Integer, j As Integer
        '注意：只有两个矩阵的长宽一样才可以进行运算

        If m1.rowbound <> m2.rowbound Or m1.colbound <> m2.colbound Then
            Debug.Print("无法进行矩阵减法运算，长宽不一致。")
            Return Nothing
        End If

        ReDim result(m1.rowbound, m1.colbound)
        ReDim md1(m1.rowbound, m1.colbound)
        ReDim md2(m2.rowbound, m2.colbound)
        m1.LetMatrix(md1, 0, 0)
        m2.LetMatrix(md2, 0, 0)

        For i = 0 To m1.rowbound
            For j = 0 To m1.colbound
                result(i, j) = md1(i, j) - md2(i, j)
            Next j
        Next i
        Return New Matrix(result)
    End Operator '重载-运算符，定义矩阵间的减法运算，计算m1 - m2

    Private Function DelRow(ByVal matrix_(,) As Double, ByVal r As Integer) As Matrix  '删除某行
        Dim i As Integer, j As Integer
        For i = r To matrix_.GetUpperBound(0) - 1
            For j = 0 To matrix_.GetUpperBound(1)
                matrix_(i, j) = matrix_(i + 1, j) '把下面的值赋给上面的值
            Next j
        Next i

        Dim result(,) As Double
        ReDim result(matrix_.GetUpperBound(0) - 1, matrix_.GetUpperBound(1)) '重新修改边界
        For i = 0 To matrix_.GetUpperBound(0) - 1
            For j = 0 To matrix_.GetUpperBound(1)
                result(i, j) = matrix(i, j)
            Next j
        Next i

        Return New Matrix(result)

    End Function '删除某一行

    Private Function DelCol(ByVal matrix_(,) As Double, ByVal c As Integer) As Matrix  '删除某列
        Dim i As Integer, j As Integer
        For i = 0 To matrix_.GetUpperBound(0)
            For j = c To matrix_.GetUpperBound(1) - 1
                matrix_(i, j) = matrix_(i, j + 1) '把右边的值赋给左边的值
            Next j
        Next i

        Dim result(,) As Double
        ReDim result(matrix_.GetUpperBound(0), matrix_.GetUpperBound(1) - 1) '重新修改边界
        For i = 0 To matrix_.GetUpperBound(0)
            For j = 0 To matrix_.GetUpperBound(1) - 1
                result(i, j) = matrix(i, j)
            Next j
        Next i

        Return New Matrix(result)

    End Function '删除某一列

    Private Sub SwapRow(ByRef matrix_(,) As Double, ByVal i As Integer, ByVal j As Integer) '交换矩阵两列
        Dim t As Integer '临时变量t，用于循环
        Dim temp As Double 'temp变量用于交换
        For t = 0 To rowbound
            temp = matrix_(i, t)
            matrix_(i, t) = matrix_(j, t)
            matrix_(j, t) = temp
        Next
    End Sub '交换某两行

    Friend Function sum()   '返回矩阵元素的和
        Dim s As Double = 0
        For i As Integer = 0 To rowbound
            For j As Integer = 0 To colbound
                s += matrix(i, j)
            Next
        Next
        Return s
    End Function

    Friend Function Conv(ByVal kernel As Matrix, Optional padding As Integer = 0)   '卷积操作
        Dim kernel_h, kernel_w As Integer
        Dim inputNew As New Matrix(matrix)
        Dim result As Matrix
        kernel_h = kernel.matrix.GetUpperBound(0)
        kernel_w = kernel.matrix.GetUpperBound(1)
        If padding < 0 Then
            Console.WriteLine("the padding should not be a negative number!")
        ElseIf padding > 0
            inputNew = New Matrix(rowbound + 2 * padding, colbound + 2 * padding)
            LetMatrix(inputNew.matrix, padding, padding)
        End If
        result = New Matrix(rowbound + 2 * padding - kernel_h + 1, colbound + 2 * padding - kernel_w + 1)
        For i As Integer = 0 To inputNew.rowbound - Int(kernel_h / 2) - 1
            For j As Integer = 0 To inputNew.colbound - Int(kernel_w / 2) - 1
                result.matrix(i, j) = (inputNew.GetPartMartix(i, i + kernel_h, j, j + kernel_w) * kernel).sum()
            Next
        Next
        Return result
    End Function

    '归一化到0-255
    Public Sub Normalization()
        Dim min, max As Double
        min = matrix(0, 0)
        max = matrix(0, 0)
        Dim newMatrix(rowbound, colbound) As Double

        'For i As Integer = 0 To rowbound
        '    For j As Integer = 0 To colbound
        '        If matrix(i, j) < 0 Then
        '            matrix(i, j) = 0
        '        End If
        '        If matrix(i, j) > 255 Then
        '            matrix(i, j) = 255
        '        End If
        '        If matrix(i, j) >= 0 And matrix(i, j) <= 63.75 Then
        '            If matrix(i, j) <= 31.875 Then
        '                matrix(i, j) /= 3.0
        '            Else
        '                matrix(i, j) /= 2.0
        '            End If
        '        End If
        '        If matrix(i, j) > 63.75 Then
        '            matrix(i, j) *= 1.5
        '            If matrix(i, j) > 255 Then
        '                matrix(i, j) = 255
        '            End If
        '        End If
        '    Next
        'Next
        For i As Integer = 0 To rowbound
            For j As Integer = 0 To colbound
                If i = 0 And j = 0 Then
                    Continue For
                End If
                If matrix(i, j) > max Then
                    max = matrix(i, j)
                End If
                If matrix(i, j) < min Then
                    min = matrix(i, j)
                End If
            Next
        Next

        For i As Integer = 0 To rowbound
            For j As Integer = 0 To colbound
                newMatrix(i, j) = 255 * (matrix(i, j) - min) / (max - min)
            Next
        Next
        matrix = newMatrix
    End Sub

    '二值化：0, 255
    Public Sub Binarization(threshold As Double)
        For i As Integer = 0 To rowbound
            For j As Integer = 0 To colbound
                If matrix(i, j) > threshold Then
                    matrix(i, j) = 255
                Else
                    matrix(i, j) = 0
                End If
            Next
        Next
    End Sub

    Public Sub abs()
        For i As Integer = 0 To rowbound
            For j As Integer = 0 To colbound
                If matrix(i, j) < 0 Then
                    matrix(i, j) = Math.Abs(matrix(i, j))
                End If
            Next
        Next
    End Sub

    '获取最大值
    Public Function Max()
        Dim i, j As Integer
        Dim maxVal As Double = matrix(0, 0)
        For m As Integer = 0 To rowbound
            For n As Integer = 0 To colbound
                If m = 0 And n = 0 Then
                    Continue For
                End If
                If matrix(m, n) > maxVal Then
                    maxVal = matrix(m, n)
                    i = m
                    j = n
                End If
            Next
        Next
        Return {maxVal, i, j}
    End Function

    '获取最小值
    Public Function Min()
        Dim i, j As Integer
        Dim minVal As Double = matrix(0, 0)
        For m As Integer = 0 To rowbound
            For n As Integer = 0 To colbound
                If m = 0 And n = 0 Then
                    Continue For
                End If
                If matrix(m, n) < minVal Then
                    minVal = matrix(m, n)
                    i = m
                    j = n
                End If
            Next
        Next
        Return {minVal, i, j}
    End Function

End Class '矩阵类，进行简单矩阵计算
